/* Copyright 2024 Coremail
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import fs from '@ohos.file.fs';
import util from '@ohos.util';
import type { Context } from '@ohos.abilityAccessCtrl';
import Stat from '@ohos.file.fs';
import { getLogger, IBuffer } from '@coremail/mail_base';

let unitArray = [`B`,'KB','MB',"GB"];
const logger = getLogger("FileUtil");
export class FileUtil {

  static sizeString(size: number): string {
    let index = 0;
    let n = size != null ? size : 0;
    while (1) {
      if (n > 1024) {
        index++;
        n = n / 1024;
      }
      else {
        break;
      }
    }
    return `${n.toFixed(2)}${unitArray[index]}`
  }


  static documentToCache(context: Context, arr: Array<string>): string[] {
    if (arr == null) {
      return [];
    }
    let caches = new Array<string>(arr.length);
    for (let i = 0;i < arr.length; i++) {
      caches[i] = FileUtil.uriToFile(context, arr[i])
    }
    return caches;
  }

  static getFileInfo(context: Context, filePath: string) :  Promise<fs.Stat> {
    let readFile = fs.openSync(filePath, fs.OpenMode.READ_ONLY);
    let fileStat = fs.stat(filePath);
    return fileStat
  }

  /**
   * 复制文件从沙盒到沙盒
   * src ==> dst
   * @param context
   * @param srcFilePath
   * @param dstFilePath
   * @returns
   */
  static copyFileSync(srcFilePath:string,dstFilePath:string): void {
    try {
      this.mkFileDirRecursively(dstFilePath);
    }
    catch (e) {
      logger.warn(JSON.stringify(e))
    }
    try {
      // let fileStat = await fs.stat(uriPath);
      // let buffer = new ArrayBuffer(fileStat.size > 4096 ? 4096 : fileStat.size);
      let buffer = new ArrayBuffer(4096);
      let readFile = fs.openSync(srcFilePath, fs.OpenMode.READ_ONLY);
      let readLen = fs.readSync(readFile.fd, buffer, { offset: 0, length: 4096 });

      let offset = 0;

      let writeFile = fs.openSync(dstFilePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
      while (readLen > 0) {
        fs.writeSync(writeFile.fd, buffer, { offset: offset, length: readLen });
        offset += readLen;
        readLen = fs.readSync(readFile.fd, buffer, { offset: offset, length: 4096 });
      }
      fs.closeSync(readFile);
      fs.closeSync(writeFile);
    }
    catch (e) {
      logger.warn(JSON.stringify(e))
    }
  }


 /**
  * 将文件从系统选择器返回的uri,拷贝到app内部的目录`$fidrDir/MailCache`下面并返回其路径
  * @param context
  * @param uriPath
  * 文件选择器返回的uri:
  * eg:
  * file://cn.coremail.hwmail/data/storage/el2/base/haps/tablet/files/1.png
  * @Param fileName
  *  拷贝后的文件名,
  * @returns
  */
   static uriToFile(context: Context, uriPath: string,fileName:string|null = null): string {
    let filesDir = context.filesDir;
    let dir = `MailCache`
    try {
      dir = FileUtil.mkdirRecursivelyIfNeeded(filesDir, dir);
    }
    catch (e) {
      logger.warn(e)
    }
    let timestamp = Date.parse(new Date().toString());
     if(!fileName) {
       //如果外部没有提供文件名,取文件路径的最后面的文件名作为文件名.
       fileName = decodeURIComponent(uriPath.substring(uriPath.lastIndexOf("/"), uriPath.length))
     }
    let filePath = `${dir}` + fileName;
    try {
      // let fileStat = await fs.stat(uriPath);
      // let buffer = new ArrayBuffer(fileStat.size > 4096 ? 4096 : fileStat.size);
      let buffer = new ArrayBuffer(4096);
      let readFile = fs.openSync(uriPath, fs.OpenMode.READ_ONLY);
      let readLen = fs.readSync(readFile.fd, buffer, { offset: 0, length: 4096 });

      let offset = 0;

      let writeFile = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
      while (readLen > 0) {
        fs.writeSync(writeFile.fd, buffer, { offset: offset, length: readLen });
        offset += readLen;
        readLen = fs.readSync(readFile.fd, buffer, { offset: offset, length: 4096 });
      }
      fs.closeSync(readFile);
      fs.closeSync(writeFile);
      return filePath;
    }
    catch (e : unknown) {
      logger.warn(e as string)
    }
     return "";
  }

  /**
   * 获取文件路径的dirPath
   */
  static getFileDirPath(filePath: string): string {
    let dirPath = filePath.substring(0,filePath.lastIndexOf("/"));
    return dirPath;
  }

  /**
   * 创建filePath
   * @param filePath
   */
  static mkFileDirRecursively(filePath: string): void {
    let dirPath = this.getFileDirPath(filePath);
    this.mkDirRecursively(dirPath);
  }

  /**
   * 从根目录开始确保文件夹存在.
   * @param newDirPath
   * @returns
   */
  static mkDirRecursively(newDirPath: string): string {
    let pathComponents = newDirPath.split('/');
    let dir = `/`
    for (let i = 0;i < pathComponents.length; i++) {
      let component = pathComponents[i];
      dir += `/${component}`
      if (fs.accessSync(dir)) {
        continue
      }
      else {
        fs.mkdirSync(dir);
      }
    }
    return dir;
  }


  static mkdirRecursivelyIfNeeded(rootDir: string, newDirPath: string): string {
    let pathComponents = newDirPath.split('/');
    let dir = `${rootDir}`
    for (let i = 0;i < pathComponents.length; i++) {
      let component = pathComponents[i];
      dir += `/${component}`
      if (fs.accessSync(dir)) {
        continue
      }
      else {
        fs.mkdirSync(dir);
      }
    }
    return dir;
  }


  static async getContentFromRawFile(context:Context,fileName: string): Promise<string> {
    let byteArray:Uint8Array = await FileUtil.getBytesFromRawFile(context,fileName);
    let content = FileUtil.uint8ArrayToString(byteArray);
    return content;
  }
  static async getBytesFromRawFile(context:Context,fileName:string) : Promise<Uint8Array> {
    let byteArray = await context.resourceManager.getRawFileContent(fileName)
    return byteArray;
  }

  static uint8ArrayToString(fileData: Uint8Array): string {
    let dataString:string = "";
    let decoder =  util.TextDecoder.create('utf-8');
    dataString = decoder.decodeWithStream(fileData);
    return dataString
  }

  static getContent(filePath:string):string {
    return fs.readTextSync(filePath);
  }

  /**
   * 将文件选择器返回的文件uri,拷贝到filePath.
   * @param context
   * @param uriPath
   * @param fileName 如果dstFilePath为uri,则必须是已经创建好的,类似文件选择器帮你创建好的.
   * @returns
   */
  static copyUriToFile(uriPath: string, filePath: string): string {
      let dirPath = filePath.substring(0, filePath.lastIndexOf("/"));
      FileUtil.mkDirRecursively(dirPath);
    let buffer = new ArrayBuffer(4096);
    let readFile = fs.openSync(uriPath, fs.OpenMode.READ_ONLY);
    let readLen = fs.readSync(readFile.fd, buffer, { offset: 0, length: 4096 });
    let offset = 0;
    let writeFile = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
    while (readLen > 0) {
      fs.writeSync(writeFile.fd, buffer, { offset: offset, length: readLen });
      offset += readLen;
      readLen = fs.readSync(readFile.fd, buffer, { offset: offset, length: 4096 });
    }
    fs.closeSync(readFile);
    fs.closeSync(writeFile);
    return filePath;
  }

  /**
   * 保存本地文件到一个uri
   * @param uriPath 已经存在的uriPath,通过文件选择器创建.
   * @param filePath
   * @returns
   */
  static copyFileToUri(filePath: string, uriPath: string): string {
    if(fs.accessSync(filePath)) {
      let buffer = new ArrayBuffer(4096);
      let readFile = fs.openSync(filePath, fs.OpenMode.READ_ONLY);
      let readLen = fs.readSync(readFile.fd, buffer, { offset: 0, length: 4096 });
      let offset = 0;
      let writeFile = fs.openSync(uriPath, fs.OpenMode.READ_WRITE);
      while (readLen > 0) {
        fs.writeSync(writeFile.fd, buffer, { offset: offset, length: readLen });
        offset += readLen;
        readLen = fs.readSync(readFile.fd, buffer, { offset: offset, length: 4096 });
      }
      fs.closeSync(readFile);
      fs.closeSync(writeFile);
    }
    return filePath;
  }



  /**
   * 写入文件到file.
   * 写入之前,需要确保文件目录全部已经被创建.
   * 只写的方式.
   * @param content
   * @param relativePath 相对于rootDir文件夹的dirpath,不包含文件名.
   * @param rootDir,系统文件夹根目录
   * @param fileName,文件名
   */
  static async writeToFile(content: IBuffer, filePath: string): Promise<string> {
    let dirPath = filePath.substring(0, filePath.lastIndexOf("/"));
    FileUtil.mkDirRecursively(dirPath)
    let file = await fs.open(filePath,fs.OpenMode.READ_WRITE|fs.OpenMode.CREATE);
    for await (const chunk of content.readRaw()) {
      const u8a = new Uint8Array(chunk.byteLength);
      u8a.set(chunk);
      await fs.write(file.fd, u8a.buffer);
    }
    await fs.close(file.fd);
    return filePath;
  }



}